package net.aiion.weave.impl.ui.xhtml;

import net.aiion.weave.spi.injection.annotations.Inject;
import net.aiion.weave.spi.naming.annotations.Named;
import net.aiion.weave.spi.naming.annotations.NotScoped;
import net.aiion.weave.spi.ui.xhtml.XhtmlException;
import net.aiion.weave.spi.xml.XmlContext;
import net.aiion.weave.spi.xml.XmlEventFactory;
import net.aiion.weave.spi.xml.XmlQualifiedName;
import net.aiion.weave.spi.xml.XmlSink;
import net.aiion.weave.spi.xml.events.ElementEndXmlEvent;
import net.aiion.weave.spi.xml.events.ElementStartXmlEvent;
import net.aiion.weave.spi.xml.events.XmlEvent;

@NotScoped
@Named
public class XhtmlStyleTransformer implements XmlSink
{
    private static final String NAMESPACE_UI_HTML = "org.weaveframework.ui.xhtml.spi";
    private static final XmlQualifiedName NAME_STYLE_CLASS = XmlEventFactory.getInstance().createQualifiedName(
        NAMESPACE_UI_HTML, "style-class");
    private static final XmlQualifiedName NAME_STYLE_RULE = XmlEventFactory.getInstance().createQualifiedName(
        NAMESPACE_UI_HTML, "style-rule");

    @Inject
    private StyleSheet styleSheet;

    private StyleClass currentStyleClass;

    public XhtmlStyleTransformer() {
    }

    @Override
    public void handleXmlEvent(final XmlContext aXmlContext) {
        final XmlEvent xmlEvent = aXmlContext.getXmlEvent();
        switch (xmlEvent.getType()) {
        case XmlEvent.START_ELEMENT:
            transformStartElement(aXmlContext, (ElementStartXmlEvent) xmlEvent);
            break;
        case XmlEvent.END_ELEMENT:
            transformEndElement(aXmlContext, (ElementEndXmlEvent) xmlEvent);
            break;
        default:
            aXmlContext.forwardEvent();
            break;
        }
    }

    private void transformStartElement(final XmlContext aXmlContext, final ElementStartXmlEvent aXmlStartElement) {
        if (NAME_STYLE_CLASS.equals(aXmlStartElement.getName())) {
            transformStyleClass(aXmlContext, aXmlStartElement);
        } else if (NAME_STYLE_RULE.equals(aXmlStartElement.getName())) {
            transformStyleRule(aXmlContext, aXmlStartElement);
        } else {
            aXmlContext.forwardEvent();
        }
    }

    private void transformStyleClass(final XmlContext aXmlContext, final ElementStartXmlEvent aXmlStartElement) {
        if (currentStyleClass != null) {
            throw new XhtmlException("Unsupported nesting of <style-class> used found");
        }

        currentStyleClass = styleSheet.createClass(aXmlStartElement.getAttributeValue("name"));
    }

    private void transformStyleRule(final XmlContext aXmlContext, final ElementStartXmlEvent aXmlStartElement) {
        if (currentStyleClass == null) {
            throw new XhtmlException("Unsupported use of <style-rule> outside of a <style-class> element found");
        }

        currentStyleClass.addRule(aXmlStartElement.getAttributeValue("key"),
            aXmlStartElement.getAttributeValue("value"));
    }

    private void transformEndElement(final XmlContext aXmlContext, final ElementEndXmlEvent aXmlEndElement) {
        if (NAME_STYLE_CLASS.equals(aXmlEndElement.getName())) {
            currentStyleClass = null;
            return;
        } else if (NAME_STYLE_RULE.equals(aXmlEndElement.getName())) {
            return;
        } else {
            aXmlContext.forwardEvent();
        }
    }
}
